Popular Searches
Popular Course Categories
Popular Courses

Designing Custom Buttons, Shapes, and Reusable UI Elements

Designing Custom Buttons, Shapes, and Reusable UI Elements

Apple Human Interface Guidelines and Advanced UI Components

Designing Custom Buttons, Shapes, and Reusable UI Elements in iOS

Course: iOS App Development

Topic: Designing Custom Buttons, Shapes, and Reusable UI Elements

Course Link: iOS App Development Training

Demo Link: Register for a Free Course Demo

JustAcademy Homepage: Visit JustAcademy


1. Introduction

Custom buttons, shapes, and reusable UI elements are essential parts of modern iOS application development. They help developers create attractive, consistent, interactive, and maintainable user interfaces. SwiftUI provides declarative tools such as Button, Shape, View, and view modifiers to build custom components with less code.

UIKit also supports custom interface design through UIButton, UIBezierPath, CAShapeLayer, and reusable UIView subclasses. This topic focuses mainly on SwiftUI, with a brief introduction to equivalent UIKit techniques.

Learning Objectives

  • Understand standard and custom buttons in SwiftUI.
  • Design buttons with custom colors, shapes, borders, icons, and animations.
  • Create geometric shapes using SwiftUI's Shape protocol.
  • Build reusable views for cards, badges, labels, and other UI elements.
  • Use properties and parameters to customize reusable components.
  • Improve accessibility, responsiveness, and code maintainability.
  • Understand when to use SwiftUI components and UIKit alternatives.

2. Understanding Buttons in SwiftUI

A button is an interactive control that performs an action when the user activates it. Buttons are commonly used for submitting forms, saving information, navigating between screens, starting processes, and confirming user choices.

SwiftUI provides the built-in Button view. Its action closure defines what happens when the button is activated, while its label describes the visible content.

Basic Button Syntax

import SwiftUI

struct BasicButtonView: View {
    var body: some View {
        Button("Tap Me") {
            print("Button tapped")
        }
    }
}

In this example, the button displays the text “Tap Me”. When activated, it prints a message in the debug console.

Button with a Custom Label

Button {
    print("Profile opened")
} label: {
    HStack(spacing: 8) {
        Image(systemName: "person.crop.circle")
        Text("View Profile")
    }
}

The label closure allows developers to combine text, system icons, images, and other SwiftUI views. This is useful when a standard text-only button does not meet the design requirements.

3. Customizing Button Appearance

SwiftUI view modifiers let you change the appearance of a button without creating an entirely new control. You can modify its foreground color, background, padding, corner radius, font, and border.

Creating a Rounded Custom Button

import SwiftUI

struct RoundedButtonView: View {
    var body: some View {
        Button {
            print("Continue tapped")
        } label: {
            Text("Continue")
                .font(.headline)
                .foregroundStyle(.white)
                .padding(.vertical, 14)
                .frame(maxWidth: .infinity)
                .background(Color.blue)
                .clipShape(RoundedRectangle(cornerRadius: 14))
        }
        .padding(.horizontal)
    }
}

Explanation of Important Modifiers

Modifier Purpose Example
font() Changes the font and text style. .font(.headline)
foregroundStyle() Sets the foreground color or style. .foregroundStyle(.white)
padding() Adds space around the content. .padding(12)
frame() Controls the available layout dimensions. .frame(maxWidth: .infinity)
background() Places a background behind the content. .background(Color.blue)
clipShape() Clips content to a specified shape. .clipShape(Capsule())
overlay() Layers another view over the existing view. .overlay(Capsule().stroke(.blue))

Button with a Capsule Shape

Button("Get Started") {
    print("Getting started")
}
.font(.headline)
.foregroundStyle(.white)
.padding(.horizontal, 28)
.padding(.vertical, 14)
.background(Color.green)
.clipShape(Capsule())

The Capsule shape creates a pill-shaped button. It is commonly used for call-to-action controls, filters, and compact actions.

4. Creating Icon Buttons

Icon buttons use symbols or images to communicate an action visually. SwiftUI supports Apple's SF Symbols through Image(systemName:). Choose symbols that are easy to recognize and provide accessibility labels when the button does not contain visible text.

struct IconButtonView: View {
    var body: some View {
        Button {
            print("Favorite selected")
        } label: {
            Image(systemName: "heart.fill")
                .font(.title2)
                .foregroundStyle(.white)
                .frame(width: 52, height: 52)
                .background(Color.pink)
                .clipShape(Circle())
        }
        .accessibilityLabel("Add to favorites")
    }
}

Common SF Symbols for Buttons

  • heart.fill — Favorite or like.
  • plus — Add a new item.
  • trash — Delete an item.
  • pencil — Edit information.
  • magnifyingglass — Search.
  • square.and.arrow.up — Share content.
  • gearshape — Settings.

Always verify that a chosen symbol is available on the minimum iOS version supported by your application.

5. Using Button Styles

SwiftUI provides button styles that control a button's overall presentation. Using styles helps keep the interface consistent and separates the button's appearance from its action.

Button("Sign In") {
    print("Sign in requested")
}
.buttonStyle(.borderedProminent)
.tint(.blue)

Other built-in styles include .bordered, .plain, and .borderless. The precise appearance can vary by platform and operating system version.

Creating a Reusable Custom ButtonStyle

struct PrimaryButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .font(.headline)
            .foregroundStyle(.white)
            .padding(.vertical, 14)
            .frame(maxWidth: .infinity)
            .background(
                configuration.isPressed
                    ? Color.blue.opacity(0.7)
                    : Color.blue
            )
            .clipShape(RoundedRectangle(cornerRadius: 12))
            .scaleEffect(configuration.isPressed ? 0.98 : 1)
            .animation(
                .easeOut(duration: 0.15),
                value: configuration.isPressed
            )
    }
}

Apply the style to multiple buttons to maintain a consistent design.

Button("Create Account") {
    print("Account creation started")
}
.buttonStyle(PrimaryButtonStyle())

Button("Continue") {
    print("Continue requested")
}
.buttonStyle(PrimaryButtonStyle())

The style reads configuration.isPressed to provide visual feedback while the user presses the button. This keeps the interaction consistent across all buttons using the same style.

6. Handling Button Actions and State

A button often updates data or changes the interface. SwiftUI's @State property wrapper allows a view to manage simple local state. When state changes, SwiftUI recalculates the affected view hierarchy.

struct CounterButtonView: View {
    @State private var count = 0

    var body: some View {
        VStack(spacing: 16) {
            Text("Count: \(count)")
                .font(.title2)

            Button("Increase Count") {
                count += 1
            }
            .buttonStyle(.borderedProminent)

            Button("Reset") {
                count = 0
            }
            .buttonStyle(.bordered)
        }
        .padding()
    }
}

Here, the first button increments the count and the second resets it. The displayed value updates automatically because it depends on the state variable.

Best Practices for Button Actions

  • Keep the action focused on one clearly defined task.
  • Validate required data before submitting it.
  • Show loading or progress feedback for long-running operations.
  • Prevent duplicate submissions when appropriate.
  • Provide clear success and error feedback.
  • Use navigation or presentation APIs when an action opens another screen.

7. Introduction to Shapes in SwiftUI

SwiftUI provides built-in shapes that can be used as backgrounds, borders, masks, decorative elements, and interface components. Shapes are scalable and can adapt to the size offered by the layout system.

Common Built-in Shapes

Shape Description Typical Use
Rectangle() A four-sided rectangular shape. Panels, backgrounds, separators.
RoundedRectangle(cornerRadius:) A rectangle with rounded corners. Cards, buttons, containers.
Circle() A circular shape. Avatars, badges, icon backgrounds.
Capsule() A pill-shaped rounded form. Chips, tags, action buttons.
Ellipse() An oval shape. Decorative backgrounds and indicators.
UnevenRoundedRectangle A rectangle with independently configurable corners. Asymmetric card designs on supported iOS versions.

Drawing a Circle

Circle()
    .fill(Color.orange)
    .frame(width: 100, height: 100)

Drawing a Rounded Rectangle

RoundedRectangle(cornerRadius: 20)
    .fill(Color.blue.opacity(0.15))
    .frame(height: 120)
    .overlay {
        RoundedRectangle(cornerRadius: 20)
            .stroke(Color.blue, lineWidth: 2)
    }

The fill() modifier fills a shape, while stroke() draws its outline. An overlay can place a border or other decorative view over the shape.

8. Creating Custom Shapes with the Shape Protocol

When built-in shapes are insufficient, implement SwiftUI's Shape protocol. A custom shape defines a path(in:) method that returns a path within the supplied rectangle.

Example: Triangle Shape

import SwiftUI

struct Triangle: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()

        path.move(to: CGPoint(x: rect.midX, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
        path.closeSubpath()

        return path
    }
}

struct TriangleExampleView: View {
    var body: some View {
        Triangle()
            .fill(Color.purple)
            .frame(width: 120, height: 100)
    }
}

Understanding the Path Methods

  • Path() creates an empty path.
  • move(to:) sets the starting point without drawing a connecting line.
  • addLine(to:) draws a straight line to a new point.
  • addQuadCurve(to:control:) draws a quadratic curve.
  • addCurve(to:control1:control2:) draws a cubic Bézier curve.
  • closeSubpath() connects the last point to the starting point.

The shape uses coordinates relative to the supplied rectangle, so it can scale to different sizes without hardcoding a particular screen dimension.

9. Using Shapes as Borders and Backgrounds

Shapes can define a component's background, border, or clipping area. Keeping the same shape definition for the background and border helps avoid inconsistent corner radii.

struct BorderedCardView: View {
    var body: some View {
        Text("Custom Card")
            .font(.headline)
            .padding(24)
            .frame(maxWidth: .infinity)
            .background(
                RoundedRectangle(cornerRadius: 16)
                    .fill(Color.blue.opacity(0.08))
            )
            .overlay {
                RoundedRectangle(cornerRadius: 16)
                    .stroke(Color.blue.opacity(0.6), lineWidth: 1.5)
            }
            .padding()
    }
}

Clipping an Image into a Shape

Image("profile-photo")
    .resizable()
    .scaledToFill()
    .frame(width: 120, height: 120)
    .clipShape(Circle())
    .overlay {
        Circle()
            .stroke(Color.white, lineWidth: 3)
    }

Ensure the image asset named profile-photo exists in the asset catalog. The frame defines the visible image area, and clipShape(Circle()) creates a circular crop.

10. Designing Reusable UI Elements

A reusable UI element is a view designed to be used in multiple locations. Instead of repeating the same layout and modifiers, developers create a component and pass the values that vary. Examples include profile cards, product tiles, status badges, custom buttons, information panels, and list rows.

Benefits of Reusable Components

  • Consistency: Components share the same visual design.
  • Maintainability: A single change can improve every use of the component.
  • Readability: Large screens can be divided into smaller, understandable views.
  • Reusability: The same view can be used on different screens.
  • Testability: Smaller components are easier to inspect and test.
  • Scalability: New screens can be built from existing UI building blocks.

11. Creating a Reusable Primary Button Component

A reusable primary button can accept a title, an optional icon, a color, and an action closure. This lets the same component support different actions while retaining a consistent appearance.

import SwiftUI

struct CustomActionButton: View {
    let title: String
    var systemImage: String? = nil
    var backgroundColor: Color = .blue
    var isDisabled: Bool = false
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            HStack(spacing: 8) {
                if let systemImage {
                    Image(systemName: systemImage)
                }

                Text(title)
                    .fontWeight(.semibold)
            }
            .foregroundStyle(.white)
            .padding(.vertical, 14)
            .frame(maxWidth: .infinity)
            .background(backgroundColor)
            .clipShape(RoundedRectangle(cornerRadius: 12))
        }
        .disabled(isDisabled)
        .opacity(isDisabled ? 0.5 : 1)
    }
}

Using the Reusable Button

struct ButtonDemoView: View {
    var body: some View {
        VStack(spacing: 16) {
            CustomActionButton(
                title: "Save Changes",
                systemImage: "checkmark",
                backgroundColor: .green
            ) {
                print("Changes saved")
            }

            CustomActionButton(
                title: "Delete Item",
                systemImage: "trash",
                backgroundColor: .red
            ) {
                print("Item deletion requested")
            }

            CustomActionButton(
                title: "Please Wait",
                isDisabled: true
            ) {
                print("This action is disabled")
            }
        }
        .padding()
    }
}

The component accepts different titles, symbols, colors, and actions. Its disabled state also makes it possible to prevent interaction when an operation is unavailable. In a production application, keep the underlying action and its validation aligned with the disabled state.

12. Creating a Reusable Information Card

Cards group related information in a visually distinct area. They can display a heading, description, icon, and optional action. A reusable card is helpful for dashboards, product screens, profile pages, and settings interfaces.

struct InformationCard: View {
    let title: String
    let description: String
    let systemImage: String

    var body: some View {
        HStack(alignment: .top, spacing: 14) {
            Image(systemName: systemImage)
                .font(.title2)
                .foregroundStyle(.blue)
                .frame(width: 44, height: 44)
                .background(Color.blue.opacity(0.12))
                .clipShape(RoundedRectangle(cornerRadius: 10))

            VStack(alignment: .leading, spacing: 6) {
                Text(title)
                    .font(.headline)

                Text(description)
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
                    .fixedSize(horizontal: false, vertical: true)
            }

            Spacer(minLength: 0)
        }
        .padding(16)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(Color(uiColor: .secondarySystemBackground))
        .clipShape(RoundedRectangle(cornerRadius: 16))
    }
}

Platform note: Color(uiColor: .secondarySystemBackground) uses UIKit's system color and is suitable for iOS. For a cross-platform SwiftUI view, choose a platform-neutral color or provide platform-specific implementations.

Using the Information Card

struct DashboardView: View {
    var body: some View {
        VStack(spacing: 16) {
            InformationCard(
                title: "Account Security",
                description: "Review your password and security settings.",
                systemImage: "lock.shield"
            )

            InformationCard(
                title: "Notifications",
                description: "Manage alerts and notification preferences.",
                systemImage: "bell.badge"
            )
        }
        .padding()
    }
}

13. Creating Reusable Status Badges

Status badges communicate short states such as Active, Pending, Completed, or Failed. A reusable badge can accept a title and color so that multiple screens use the same visual treatment.

struct StatusBadge: View {
    let title: String
    var color: Color = .green

    var body: some View {
        Text(title)
            .font(.caption.weight(.semibold))
            .foregroundStyle(color)
            .padding(.horizontal, 10)
            .padding(.vertical, 6)
            .background(color.opacity(0.12))
            .clipShape(Capsule())
            .accessibilityElement(children: .combine)
    }
}

Example Usage

HStack(spacing: 10) {
    StatusBadge(title: "Active", color: .green)
    StatusBadge(title: "Pending", color: .orange)
    StatusBadge(title: "Failed", color: .red)
}

Do not rely on color alone to communicate a status. The visible text should make the meaning understandable to users with color-vision differences or accessibility settings.

14. Organizing Reusable Components

As an application grows, place reusable components in separate Swift files. This keeps screen implementations focused on composition and business logic rather than repeated styling.

MyIOSApp/
├── App/
│   └── MyIOSApp.swift
├── Screens/
│   ├── HomeView.swift
│   ├── ProfileView.swift
│   └── SettingsView.swift
├── Components/
│   ├── CustomActionButton.swift
│   ├── InformationCard.swift
│   ├── StatusBadge.swift
│   └── Triangle.swift
├── Models/
│   └── User.swift
└── Assets.xcassets/

This is an example organization, not a required Xcode structure. Small projects may use fewer folders, while larger projects may group components by feature or design system.

Guidelines for Component Design

  • Give each component one clear responsibility.
  • Use descriptive names such as PrimaryActionButton or ProfileSummaryCard.
  • Expose only the properties that consumers need to configure.
  • Use sensible defaults for optional appearance settings.
  • Keep business logic outside purely visual components when practical.
  • Use shared design tokens for colors, spacing, typography, and corner radii in larger applications.
  • Avoid creating reusable components for layouts that are unlikely to be reused unless extraction improves readability.

15. Responsive Layout and Dynamic Content

Reusable components should work with different text lengths, device sizes, and accessibility settings. Hardcoded widths can cause clipping on smaller screens or when users increase the text size.

Responsive Button Example

Button("Continue to Checkout") {
    print("Checkout started")
}
.font(.headline)
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.foregroundStyle(.white)
.clipShape(RoundedRectangle(cornerRadius: 12))

The flexible frame allows the button to expand within the available parent width. The parent view should still provide appropriate horizontal padding and layout constraints.

Responsive Design Practices

  • Prefer flexible frames and stacks over unnecessary fixed widths.
  • Use ViewThatFits when alternative layouts are needed for different available sizes.
  • Test with long titles, translated text, and larger Dynamic Type sizes.
  • Check portrait and landscape orientations where supported.
  • Use system colors where suitable to support light and dark appearances.
  • Ensure interactive controls remain comfortably sized and do not overlap.

16. Accessibility for Custom Buttons and Shapes

Custom UI should remain understandable and usable with VoiceOver, Switch Control, Dynamic Type, and other accessibility features. A visually attractive button is not complete if assistive technologies cannot identify its purpose.

Accessibility Example

Button {
    print("Item added to favorites")
} label: {
    Image(systemName: "heart.fill")
        .font(.title2)
        .foregroundStyle(.pink)
        .frame(width: 48, height: 48)
        .contentShape(Rectangle())
}
.accessibilityLabel("Add to favorites")
.accessibilityHint("Adds this item to your favorites")

Accessibility Checklist

  • Provide a meaningful accessibility label for icon-only controls.
  • Use an accessibility hint when it clarifies the result of an action.
  • Maintain sufficient contrast between text, backgrounds, and borders.
  • Do not use color as the only indicator of success, failure, or selection.
  • Support text scaling and avoid truncating important information.
  • Keep the interactive area large enough for comfortable tapping; aim for Apple's commonly recommended 44-by-44-point minimum where practical.
  • Verify keyboard and assistive-technology interaction when relevant to the app.

17. Adding Animation and Interaction Feedback

Subtle animations can make custom buttons feel responsive. SwiftUI supports state-driven animation and interaction feedback. Animations should communicate a change without slowing down common actions or distracting users.

struct AnimatedFavoriteButton: View {
    @State private var isFavorite = false

    var body: some View {
        Button {
            withAnimation(.spring(response: 0.3, dampingFraction: 0.55)) {
                isFavorite.toggle()
            }
        } label: {
            Image(systemName: isFavorite ? "heart.fill" : "heart")
                .font(.system(size: 32))
                .foregroundStyle(isFavorite ? .pink : .secondary)
                .scaleEffect(isFavorite ? 1.15 : 1)
        }
        .accessibilityLabel(
            isFavorite ? "Remove from favorites" : "Add to favorites"
        )
    }
}

The state variable determines which symbol appears. The animation makes the transition feel smoother when the user changes the favorite state.

18. UIKit Alternatives for Custom UI

SwiftUI is the main focus of this topic, but some applications use UIKit or combine both frameworks. UIKit offers similar capabilities using views, layers, and drawing APIs.

Customizing a UIButton

import UIKit

func makePrimaryButton() -> UIButton {
    var configuration = UIButton.Configuration.filled()
    configuration.title = "Continue"
    configuration.cornerStyle = .large
    configuration.baseBackgroundColor = .systemBlue
    configuration.baseForegroundColor = .white
    configuration.contentInsets = NSDirectionalEdgeInsets(
        top: 14,
        leading: 24,
        bottom: 14,
        trailing: 24
    )

    return UIButton(configuration: configuration)
}

UIButton.Configuration provides a modern way to define button appearance. The button's action can be connected using target-action or other supported UIKit event-handling approaches.

Creating a Circular UIView

import UIKit

let avatarView = UIView()
avatarView.backgroundColor = .systemOrange
avatarView.layer.cornerRadius = 40
avatarView.clipsToBounds = true

For a square view measuring 80 by 80 points, a corner radius of 40 points produces a circular appearance. In a real layout, ensure the view's final bounds are correct before relying on its corner radius.

SwiftUI and UIKit Comparison

Feature SwiftUI UIKit
Button Button UIButton
Reusable component A custom View A custom UIView
Custom shape Shape protocol and Path UIBezierPath or CAShapeLayer
Local UI state @State Properties and explicit UI updates
Visual styling View modifiers and styles View properties and layer configuration
Layout Stacks, grids, and layout containers Auto Layout and stack views

19. Common Errors and Troubleshooting

Problem Possible Cause Solution
Button does not respond. The action is empty, disabled, or blocked by another view. Check the action closure, disabled state, and hit-testing behavior.
Button label is clipped. Fixed dimensions or insufficient space. Use flexible frames, appropriate padding, and test longer text.
Rounded corners do not appear. The wrong view is being clipped or the background is applied after clipping. Apply the background and shape modifiers in the intended order; use clipShape() when clipping is required.
Custom shape looks distorted. Path coordinates assume a fixed size. Calculate points using the supplied rect dimensions.
Reusable component cannot be configured. Required values are hardcoded. Expose suitable properties or initializer parameters.
Icon-only button is unclear to VoiceOver. No meaningful accessibility label. Add accessibilityLabel() and, where useful, accessibilityHint().
Button can be tapped repeatedly during submission. No in-progress state or duplicate-action protection. Track loading state, disable the control while appropriate, and protect the underlying operation.
Colors are difficult to see in dark mode. Fixed colors do not adapt to the current appearance. Prefer semantic system colors or define light and dark color assets.

20. Practical Exercise: Build a Reusable Product Card

Objective: Create a product card using a reusable view, a custom button, and built-in shapes.

Requirements

  1. Create a SwiftUI view named ProductCardView.
  2. Display a product name, a short description, and a price.
  3. Use a rounded rectangle for the card background.
  4. Add an icon or image representing the product.
  5. Create an “Add to Cart” button using a reusable button component.
  6. Show a status badge such as “Available”.
  7. Use state to display a confirmation or update the cart count after activation.
  8. Add accessibility labels where needed.
  9. Test the design with different product names and descriptions.

Sample Implementation

struct ProductCardView: View {
    let name: String
    let description: String
    let price: String

    @State private var isAddedToCart = false

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Image(systemName: "shippingbox")
                .font(.system(size: 36))
                .foregroundStyle(.blue)
                .frame(maxWidth: .infinity)
                .frame(height: 100)
                .background(Color.blue.opacity(0.08))
                .clipShape(RoundedRectangle(cornerRadius: 12))

            StatusBadge(title: "Available", color: .green)

            Text(name)
                .font(.headline)

            Text(description)
                .font(.subheadline)
                .foregroundStyle(.secondary)

            Text(price)
                .font(.title3.weight(.bold))

            CustomActionButton(
                title: isAddedToCart ? "Added to Cart" : "Add to Cart",
                systemImage: isAddedToCart ? "checkmark" : "cart",
                backgroundColor: isAddedToCart ? .green : .blue
            ) {
                isAddedToCart = true
            }
        }
        .padding(16)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(Color(uiColor: .secondarySystemBackground))
        .clipShape(RoundedRectangle(cornerRadius: 18))
    }
}

Previewing the Component

#Preview {
    ProductCardView(
        name: "Wireless Headphones",
        description: "Comfortable headphones for everyday listening.",
        price: "₹2,499"
    )
    .padding()
}

This example assumes that CustomActionButton and StatusBadge are available in the project. In a real shopping application, the cart action should update the application's cart data rather than only changing the button label.

21. Interview Questions and Answers

Q1. What is a custom button in SwiftUI?

A custom button is a standard SwiftUI Button whose label and appearance are customized using views, modifiers, or a reusable ButtonStyle.

Q2. What is the difference between Button and ButtonStyle?

Button defines the interactive control and its action. ButtonStyle defines how buttons using that style are presented and how they respond visually to interaction.

Q3. What is the Shape protocol?

The Shape protocol allows developers to define geometric shapes by implementing path(in:), which returns a path drawn within a supplied rectangle.

Q4. What is the purpose of clipShape()?

clipShape() clips a view's rendered content to the specified shape, such as a circle or rounded rectangle.

Q5. What is the difference between fill() and stroke()?

fill() fills the interior of a shape, while stroke() draws the shape's outline.

Q6. What makes a UI component reusable?

A reusable component encapsulates a defined piece of UI and accepts configurable data or actions, allowing it to be used in multiple places without duplicating its implementation.

Q7. Why should reusable components accept parameters?

Parameters allow the same component to display different titles, colors, icons, values, and actions while preserving consistent layout and styling.

Q8. How can an icon-only button be made accessible?

Use a meaningful accessibilityLabel(), provide an accessibility hint if needed, and ensure the button has a practical interaction area.

Q9. How do you add press feedback to a custom button?

A custom ButtonStyle can use configuration.isPressed to change scale, opacity, or background appearance during interaction.

Q10. When should a developer create a custom Shape?

Create a custom shape when built-in shapes cannot represent the required design, such as a triangle, custom pointer, or decorative geometric form.

Q11. How does @State help with custom UI?

@State stores local view state. Changing its value causes SwiftUI to update views that depend on that value.

Q12. What is the benefit of a reusable design system?

A design system centralizes common colors, typography, spacing, and component styles, making the application more consistent and easier to maintain.

22. Best Practices Summary

  • Use built-in SwiftUI controls and shapes whenever they meet the design requirement.
  • Build reusable views when a component has a clear responsibility or appears in multiple places.
  • Use custom ButtonStyle implementations for consistent button appearance and interaction feedback.
  • Calculate custom shape paths relative to the supplied rectangle.
  • Keep visual components configurable without exposing unnecessary complexity.
  • Use semantic colors and support light and dark appearances.
  • Test layouts with different screen sizes, text lengths, and Dynamic Type settings.
  • Provide clear accessibility labels and meaningful interaction feedback.
  • Separate presentation from business logic when practical.
  • Verify custom controls on simulators and real devices before release.

23. Quick Revision Checklist

  • Understand the SwiftUI Button action and label closures.
  • Know how to customize padding, colors, backgrounds, borders, and corner shapes.
  • Understand built-in shapes such as Circle, Capsule, Rectangle, and RoundedRectangle.
  • Know how to implement a custom Shape using Path.
  • Understand reusable views, parameters, default values, and action closures.
  • Know how @State updates a view after user interaction.
  • Understand button styles, press feedback, and animation.
  • Apply accessibility labels and responsive layout techniques.
  • Recognize UIKit alternatives such as UIButton, UIView, and CAShapeLayer.
  • Build and test a reusable product card or dashboard component.

Conclusion

Designing custom buttons, shapes, and reusable UI elements is a key skill in iOS app development. SwiftUI makes it possible to create polished controls using declarative views, built-in shapes, custom styles, and reusable components. By combining flexible layouts, clear state management, accessibility, and consistent styling, developers can build interfaces that are easier to use, test, and maintain.

For more information about iOS app development training, visit JustAcademy iOS App Development Training, register for a course demo, or explore the JustAcademy website.

Previous Next
whatsapp